/*
 * Copyright (C) 2010-2011 Comcast Cable.  All Rights Reserved.
 */
package com.hercules.android.receiver.handlers.commands.call.animation.interpolators;


/**
 * This interpolator implements ELASTIC_IN, ELASTIC_OUT and ELASTIC_IN_OUT easing functions defined by XRE
 * specification. It extends BaseInterpolator.
 * <p>
 * <b>Thread Safety:</b> This class is immutable and thread-safe.
 *
 * @author semi_sleep, 7realm
 * @version 1.0
 */
public class ElasticInterpolator extends BaseInterpolator {
    /**
     * Creates an instance of ElasticInterpolator using the given in and out flag.
     *
     * @param in the flag indicates whether "ease in" motion should be provided
     * @param out the flag indicates whether "ease out" motion should be provided
     * @throws IllegalArgumentException if both in and out flags are false
     */
    public ElasticInterpolator(boolean in, boolean out) {
        super(in, out);
    }

    /**
     * Maps a point on the timeline to a multiplier to be applied to the transformations of an animation. This method
     * implements motion defined by an exponentially decaying sine wave.
     * <p>
     * The algorithm is ported from http://opensource.adobe.com/svn/opensource/flex/sdk/branches/3.4.0/frameworks
     * /projects/framework/src/mx/effects/easing/Elastic.as
     *
     * @param input a value between 0 and 1.0 indicating our current point in the animation where 0 represents the start
     * and 1.0 represents the end
     * @return the interpolation value, this value can be more than 1.0 for Interpolators which overshoot their targets,
     * or less than 0 for Interpolators that undershoot their targets
     * @throws IllegalArgumentException if input is not in [0, 1]
     */
    public float getInterpolation(float input) {
        // check argument
        checkRange(input);

        if (input == 0 || input == 1) {
            return input;
        }

        double result;
        if (isIn() && isOut()) {
            input *= 2;
            input--;
            if (input < 0) {
                result = -Math.pow(2, 10 * input) * Math.sin((input - 0.1125) * 2 * Math.PI / 0.45);
            } else {
                result = 2 + Math.pow(2, -10 * input) * Math.sin((input - 0.1125) * 2 * Math.PI / 0.45);
            }
            result /= 2;
        } else if (isIn()) {
            input--;
            result = -Math.pow(2, 10 * input) * Math.sin((input - 0.075) * 2 * Math.PI / 0.3);
        } else {
            result = 1 + Math.pow(2, -10 * input) * Math.sin((input - 0.075) * 2 * Math.PI / 0.3);
        }
        return (float) result;
    }
}